﻿namespace UnityEditor.Controls.GraphicsControl
{
    using System;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interop;
    using System.Windows.Media;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using UnityEditor.Layout;
    using UnityEngine.Core;

    /// <summary>
    /// Interaction logic for XNAGraphicsControl.xaml
    /// </summary>
    public partial class GraphicsControl : UserControl
    {
        private GraphicsGame game;

        private RenderTarget2D renderTarget;

        public GraphicsControl()
        {
            InitializeComponent();
        }

        public GraphicsGame Game
        {
            get
            {
                return game;
            }
            set
            {
                game = value;
                this.ResetDevice();
            }
        }

        public void BeginD3DRendering()
        {
            if (renderTarget != null)
            {
                Game.GraphicsDevice.SetRenderTarget(null);
                renderTarget.Dispose();
            }

            int width = image.ActualWidth > 0.0f ? (int)image.ActualWidth : (int)this.ActualWidth;
            int height = image.ActualHeight > 0.0f ? (int)image.ActualHeight : (int)this.ActualHeight;
            renderTarget = new RenderTarget2D(GraphicsHelper.GetGraphicsDeviceManager(Game).GraphicsDevice, width, height);
            Game.GraphicsDevice.SetRenderTarget(renderTarget);

            GraphicsHelper.CallGameMethod(Game, "Initialize", new object[] { });

            // Get our Direct3DSurface9 pointer
            IntPtr pRenderTargetSurface = GraphicsHelper.GetRenderTargetSurface(renderTarget);

            // d3dImage is type D3DImage
            d3dImage.Lock();
            d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, pRenderTargetSurface);
            d3dImage.Unlock();

            CompositionTarget.Rendering += OnRendering;
        }

        public void HandleDeviceReset()
        {
            bool deviceNeedsReset = false;

            switch (GraphicsHelper.GetGraphicsDeviceManager(Game).GraphicsDevice.GraphicsDeviceStatus)
            {
                case GraphicsDeviceStatus.Lost:
                    // If the graphics device is lost, we cannot use it at all.
                    Debug.WriteLine("Graphics device lost");
                    return;

                case GraphicsDeviceStatus.NotReset:
                    // If device is in the not-reset state, we should try to reset it.
                    deviceNeedsReset = true;
                    break;

                default:
                    // If the device state is ok, check whether it is big enough.
                    PresentationParameters pp = GraphicsHelper.GetGraphicsDeviceManager(Game).GraphicsDevice.PresentationParameters;

                    deviceNeedsReset = ((int)image.ActualWidth > pp.BackBufferWidth) ||
                                       ((int)image.ActualHeight > pp.BackBufferHeight);
                    break;
            }

            // Do we need to reset the device?
            if (deviceNeedsReset)
            {
                ResetDevice();
            }
        }

        public void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (d3dImage.IsFrontBufferAvailable)
            {
                BeginD3DRendering();
            }
            else
            {
                StopD3DRendering();
            }
        }

        public void OnLoaded(object sender, EventArgs e)
        {
            Time.Initialize();
            StartD3DRendering();
            Workspace.Instance.GameChanged +=new EventHandler(Instance_GameChanged);
        }

        public void ResetDevice()
        {
            try
            {
                StopD3DRendering();
                Game.Resize((int)image.ActualWidth, (int)image.ActualHeight);
                GraphicsHelper.CallGameMethod(Game, "Initialize", new object[] { });
                BeginD3DRendering();

            }
            catch (Exception e)
            {
                Debug.WriteLine("Graphics device reset failed\n\n" + e);
            }
        }

        public void StartD3DRendering()
        {
            if (d3dImage.IsFrontBufferAvailable)
            {
                // d3dImage = new D3DImage();
                d3dImage.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged;

                BeginD3DRendering();
            }
        }

        public void StopD3DRendering()
        {
            if (renderTarget != null)
            {
                Game.GraphicsDevice.SetRenderTarget(null);
                renderTarget.Dispose();
            }

            GraphicsHelper.CallGameMethod(Game, "Initialize", new object[] { });

            CompositionTarget.Rendering -= OnRendering;
        }

        private void image_Unloaded(object sender, RoutedEventArgs e)
        {
            if (Game != null)
            {
                StopD3DRendering();
                Game.GameScene = null;
            }
        }

        void OnRendering(object sender, EventArgs e)
        {
            if (Workspace.Instance.GameStatus == GameStatus.Pause ||  
                Workspace.Instance.GameStatus == GameStatus.PlayInWindow)
                return;

            // HandleDeviceReset();
            d3dImage.Lock();
            IntPtr pRenderTargetSurface = GraphicsHelper.GetRenderTargetSurface(renderTarget);
            d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, pRenderTargetSurface);

            Game.GameScene = Workspace.Instance.GameScene;
            this.Game.ClearBackground(Microsoft.Xna.Framework.Color.BurlyWood);
            Time.Tick();
            GameTime gameTime = new GameTime(Time.TotalTime, Time.DeltaTime);

            if (Workspace.Instance.GameStatus == GameStatus.Startup)
            {
                GraphicsHelper.CallGameMethod(Game, "Startup", new object[] { gameTime });
                Workspace.Instance.GameStatus = GameStatus.Play;
            }
            if (Workspace.Instance.GameStatus == GameStatus.Play ||
                Workspace.Instance.GameStatus == GameStatus.Editor)
            {
                GraphicsHelper.CallGameMethod(Game, "Update", new object[] { gameTime });
                GraphicsHelper.CallGameMethod(Game, "Draw", new object[] { gameTime });
            }

            d3dImage.AddDirtyRect(new Int32Rect(0, 0, d3dImage.PixelWidth, d3dImage.PixelHeight));
            d3dImage.Unlock();
        }

        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (Game != null)
            {
                ResetDevice();
            }
        }

        private void Instance_GameChanged(Object sender, EventArgs e)
        {
            StopD3DRendering();
            this.Game = Workspace.Instance.GameGraphicsGame;
            StartD3DRendering();
        }
    }
}
